﻿#region using
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using System.Xml;
using System.Xml.Schema;

using Microsoft.VisualStudio;
// using Microsoft.VisualStudio.TextTemplating.VSHost;
using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.Win32;

using XmlGenSharp;
using XmlGenSharp.Logic.Main;
#endregion

namespace XmlGenSharp.App.VSCustomTool
{
  [Guid("E418E90F-CB02-4bd2-87F7-5980D30ADC7A")]
  [CustomTool("XmlGenSharpMulti", "XML Data Binding Generator for C#.")]
  public class XmlGenSharpMultiFile : VsMultipleFileGenerator
  {
    // Member Variables & Constants

    #region Member Variables & Constants
    internal static Guid CSharpCategoryGuid = new Guid("FAE04EC1-301F-11D3-BF4B-00C04F79EFBC");
    private const string VisualStudioVersion = "9.0";
    #endregion

    // Constructors

    #region XmlGenSharpMultiFile()
    public XmlGenSharpMultiFile()
    {
      BindingGenerator = new BindingGenerator();
      PrimaryGenerator = BindingGenerator;

      SecondaryGenerators = new List<IGenerator>();
    }
    #endregion

    // Private Properties

    #region BindingGenerator
    private BindingGenerator BindingGenerator
    {
      get;
      set;
    }
    #endregion

    // Static COM-Related Methods

    #region static void RegisterClass(Type t)
    [ComRegisterFunction]
    public static void RegisterClass(Type t)
    {
      GuidAttribute guidAttribute = getGuidAttribute(t);
      CustomToolAttribute customToolAttribute = getCustomToolAttribute(t);
      using (RegistryKey key = Registry.LocalMachine.CreateSubKey(
        GetKeyName(CSharpCategoryGuid, customToolAttribute.Name)))
      {
        key.SetValue("", customToolAttribute.Description);
        key.SetValue("CLSID", "{" + guidAttribute.Value + "}");
        key.SetValue("GeneratesDesignTimeSource", 1);
      }
    }
    #endregion

    #region static void UnregisterClass(Type t)
    [ComUnregisterFunction]
    public static void UnregisterClass(Type t)
    {
      CustomToolAttribute customToolAttribute = getCustomToolAttribute(t);
      Registry.LocalMachine.DeleteSubKey(GetKeyName(
        CSharpCategoryGuid, customToolAttribute.Name), false);
    }
    #endregion

    #region getGuidAttribute(Type t)
    internal static GuidAttribute getGuidAttribute(Type t)
    {
      return (GuidAttribute)getAttribute(t, typeof(GuidAttribute));
    }
    #endregion

    #region getCustomToolAttribute(Type t)
    internal static CustomToolAttribute getCustomToolAttribute(Type t)
    {
      return (CustomToolAttribute)getAttribute(t, typeof(CustomToolAttribute));
    }
    #endregion

    #region getAttribute(Type t, Type attributeType)
    internal static Attribute getAttribute(Type t, Type attributeType)
    {
      object[] attributes = t.GetCustomAttributes(attributeType, /* inherit */ true);
      if (attributes.Length == 0)
        throw new Exception(
          String.Format("Class '{0}' does not provide a '{1}' attribute.",
          t.FullName, attributeType.FullName));
      return (Attribute)attributes[0];
    }
    #endregion

    #region GetKeyName(Guid categoryGuid, string toolName)
    internal static string GetKeyName(Guid categoryGuid, string toolName)
    {
      return
        String.Format("SOFTWARE\\Microsoft\\VisualStudio\\" + VisualStudioVersion +
          "\\Generators\\{{{0}}}\\{1}\\", categoryGuid, toolName);
    }
    #endregion

    // Public Methods

    #region DefaultExtension(out string pbstrDefaultExtension)
    public override int DefaultExtension(out string pbstrDefaultExtension)
    {
      if (PrimaryGenerator == null)
        pbstrDefaultExtension = ".Generator.cs";
      else
        pbstrDefaultExtension = PrimaryGenerator.GetExtension();

      return pbstrDefaultExtension.Length;
    }
    #endregion

    #region GetDefaultExtension()
    public override string GetDefaultExtension()
    {
      string ext;
      DefaultExtension(out ext);

      return ext;
    }
    #endregion

    #region Generate(string wszInputFilePath, string bstrInputFileContents, ...)
    public override int Generate(string wszInputFilePath, string bstrInputFileContents,
      string wszDefaultNamespace, IntPtr[] rgbOutputFileContents,
      out uint pcbOutput, IVsGeneratorProgress pGenerateProgress)
    {
      Trace.TraceInformation("Inside MultiFileCustomTool.Generate()");

      if (wszInputFilePath == null || wszInputFilePath == string.Empty)
      {
        pcbOutput = 0;
        return VSConstants.S_OK;
      }

      BindingAnalyzer = new BindingAnalyzer(SolutionDir, wszInputFilePath, wszDefaultNamespace);
      MultiOutputAnalyzerContext moac = new MultiOutputAnalyzerContext(BindingAnalyzer);
      BindingAnalyzer.Analyze(moac);

      PrimaryGenerator = moac.PrimaryGenerator;
      this.SecondaryGenerators = moac.SecondaryGenerators;

      int result = base.Generate(wszInputFilePath, bstrInputFileContents, wszDefaultNamespace, rgbOutputFileContents, out pcbOutput, pGenerateProgress);

      Trace.TraceInformation("Exiting MultiFileCustomTool.Generate()");

      return result;
    }
    #endregion
  }
}
